Skip to content

住所から郵便番号を探す機能と、事業所の個別郵便番号データへの対応を追加 - #10

Merged
cssho merged 5 commits into
masterfrom
address-to-zip
Nov 5, 2026
Merged

cssho merged 5 commits into
masterfrom
address-to-zip

Conversation

@cssho

@cssho cssho commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

概要

README の備考にあった「住所→郵便番号変換辞書」を実装しました。あわせて、日本郵便の事業所の個別郵便番号データ(JIGYOSYO.CSV)も読み込めるようにしました。データはどちらも同梱せず、利用者側で指定します。

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // 事業所データ(Shift_JIS)用。.NET Framework では不要
var dictionary = ZipCodeDictionary.Load(@"c:\data\utf_ken_all.zip", @"c:\data\jigyosyo.zip");

dictionary.TryGetZipCode("東京都千代田区霞が関3-2-5 霞が関ビル10階", out var zip);           // → "1006010"
dictionary.TryGetZipCode("東京都千代田区大手町2-3-1", "日本郵便株式会社", out var businessZip); // → "1008792"

追加した API

API 内容
dictionary.FindByAddress(住所) 郵便番号の候補の町域を返す。1 つに決まらない場合は複数
dictionary.FindByAddress(住所, 事業所名) 事業所の個別郵便番号を探す。見つからなければ住所だけで探す
dictionary.TryGetZipCode(住所[, 事業所名], out 郵便番号) 郵便番号が 1 つに決まったときだけ true
ZipCodeDictionary.Load(KEN_ALLのパス, JIGYOSYOのパス) / Load(Stream, Stream, Encoding) 事業所の個別郵便番号データも読み込む(zip のままでも可)
ZipCodeEntry.Kind / BusinessName / Block / IsIndividual 郵便番号の種類(町域・大口事業所・私書箱)、事業所名、番地等

住所から郵便番号を探す方法

  1. 都道府県 + 市区町村を照合する。同名の市区町村(府中市など)は、町域名まで一致した方を使う。
  2. その市区町村の中で、町域名がいちばん長く一致した町域を選ぶ。
  3. 同じ町域名で郵便番号が複数ある場合は、町域名の括弧書きで絞り込む。
    • 超高層ビル(「霞が関ビル(10階)」):住所の番地の後ろのビル名と、その後の「10階」「10F」で選ぶ。階がない・地下・データにない階は「(地階・階層不明)」、ビル名がなければ「(次のビルを除く)」など。
    • 地区名(「音江町(稲田)」「音江町(国見23〜298番地)」):町域名の直後の地区名で選ぶ。
    • 丁目・番地の範囲(「北六条東(1〜7丁目)」):町域名(または地区名)の直後の丁目・番地で選ぶ。当てはまらなければ「(その他)」。
  4. 町域名が一致しない場合は、マニュアルに従って代表の郵便番号(「以下に掲載がない場合」)を返す。

事業所名を渡した場合は、住所の市区町村にある事業所から、事業所名(空白・全角半角・「株式会社/(株)」の違いは区別しない)が一致するものを探します。同じ名前が複数あれば、番地等がいちばん長く一致するもの(私書箱番号を含む)を選びます。

照合の改善(住所の分割にも効く)

  • 漢数字と算用数字を同一視するようにした(「北六条東」と「北6条東」)。
  • 郡名の省略に対応した(「東神楽町…」)。「郡山市」「大和郡山市」は対象外。
  • 超高層ビルの住所は、町域名(「霞が関」)の後ろで分割するようにした。抜き出されるバーコードの内容は変わらない。

個別郵便番号のバーコード

マニュアル p22 に従い、大口事業所・私書箱の個別郵便番号は個別番号のみをバーコード化し、住所表示番号の 13 けたは CC4 で埋めます(事業所データを読み込んだ場合)。

実データでの確認

2026年10月更新の utf_ken_all.csv と JIGYOSYO.CSV で、データから組み立てた住所を検索しました。実際の住所の書き方の揺れまでは確かめていません。

種類 件数 1 つに決まり正解
町域全体 122,657 99.2%(候補が複数で正解を含む 0.7%)
 超高層ビル 3,389 すべて
 地区名 1,557 99.2%
 丁目・番地の範囲 302 すべて
事業所(住所 + 事業所名) 22,420 99.6%(誤り・見つからないは 0 件。残りの多くは個別番号を複数持つ事業所)
  • 町域の残り 0.04% は、組み立てた住所の番地が同じ町域の別の郵便番号の範囲に入ったものや、「庶路(宮下)」と「庶路宮下」のようにデータ自体が紛らわしいもの。
  • 個別郵便番号 22,420 件すべてで、バーコードの住所表示番号が CC4 で埋まることを確認。
  • 検索は 1 件 24.6μs、読み込みは約 0.4 秒。メモリは町域だけで約 24MB、事業所データも読み込むと約 44MB。

まだできないこと(README に明記)

  • 京都市の通り名を含む住所(「烏丸通御池上る二条殿町」など)は、町域名の前に通り名があるため代表の郵便番号になる。
  • 地区名が書かれていない住所、括弧書きを解釈できない町域、個別番号を複数持つ事業所は、候補が複数になる。

コードレビューへの対応

  • 事業所名の部分一致は、住所が番地等まで一致する場合のみ採用するようにした(「田中」で「田中貴金属工業」の個別郵便番号を返していた)。
  • 郵便番号データ・事業所の個別郵便番号データの都道府県名を確認し、文字コードが違うファイル(UTF-8 に変換した JIGYOSYO.CSV など)を読み込んだ場合は例外にした。
  • 同名の市区町村は、町域名がより長く一致した方を優先するようにした(「○○村一円」の村と、町域名まで一致した村が引き分けになっていた)。
  • 住所・ビル名・事業所名の照合で、英字の大文字・小文字を区別しないようにした。
  • 「大字」「字」の照合位置を住所ごとに 1 回だけ求めるようにし、最長一致の処理・市区町村の型・漢数字の一覧の重複をまとめた。
  • 読み込み時の処理を初回検索まで遅らせる案と、住所の分割で正規化済みの候補を使い回す案は、効果が小さい(読み込み約 0.2 秒、分割 1 件約 3μs)ため見送った。
  • 修正後も実データでの精度は変わらない(町域 99.2%、事業所 99.6%)。

テスト

  • dotnet test で 237 件すべて成功(61 件追加)。
  • 事業所のテスト用データ TestData/jigyosyo_sample.csv は実データと同じ Shift_JIS のため、.gitattributes で binary 指定した。

🤖 Generated with Claude Code

https://claude.ai/code/session_01HNb8H3ydQTR8dWU8d28osE

cssho and others added 4 commits November 5, 2026 22:59
- FindByAddress / TryGetZipCode を追加し、住所から郵便番号を探せるようにした
  - 町域名の括弧書き(丁目・番地の範囲、超高層ビルの階、地区名、その他)で絞り込む
  - 同名の市区町村は町域名まで一致した方、町域名が一致しなければ代表の郵便番号
- 住所の照合で、漢数字と算用数字(北六条 と 北6条)、郡名の省略を区別しないようにした
- 事業所の個別郵便番号データ(JIGYOSYO.CSV、Shift_JIS)を読み込めるようにした
  - 住所と事業所名から個別郵便番号を探せる
  - 個別郵便番号のバーコードは住所表示番号を CC4 で埋める(マニュアル p22)
- README と CLAUDE.md を更新

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNb8H3ydQTR8dWU8d28osE
- 事業所名の部分一致は、住所が番地等まで一致する場合のみ採用するようにした
  (「田中」が「田中貴金属工業」の個別郵便番号になっていた)
- 郵便番号データ・事業所の個別郵便番号データの都道府県名を確認し、
  文字コードが違うファイルを読み込んだ場合は例外にした
- 同名の市区町村は、町域名がより長く一致した方を優先するようにした
- 住所・ビル名・事業所名の照合で、英字の大文字・小文字を区別しないようにした
- 「大字」「字」の照合位置を住所ごとに1回だけ求めるようにし、最長一致の処理と市区町村の型をまとめた
- 漢数字の一覧を BarCode にまとめた
- テスト用データの架空の都道府県名を実在のものにした

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNb8H3ydQTR8dWU8d28osE
char.ToUpperInvariant は住所の分割で1件あたり約1μs(1.9μs→3.0μs)遅くなっていたため、
ToHankaku 後の半角 a~z だけを大文字にするようにした。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNb8H3ydQTR8dWU8d28osE
- net8.0 では、住所照合の正規化をスタック上で行い、CSV の読み込みで使う列だけを文字列にする
- 住所の分割を都道府県名・市区町村名・町域名の順の照合に変え、候補文字列を作らないようにした(両方のビルド)
- 郵便番号のチェックを LINQ・正規表現を使わないループにした(両方のビルド)
- テストでコアのビルドを切り替えられるようにした(dotnet test -p:CoreTargetFramework=netstandard2.0)
- README と CLAUDE.md を更新

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HNb8H3ydQTR8dWU8d28osE
コアを netstandard2.0 と net8.0 のマルチターゲットにし、net8.0 では Span<T> を使う
@cssho
cssho merged commit 6aa64fd into master Nov 5, 2026
@cssho
cssho deleted the address-to-zip branch November 5, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant